package com.xncoding.netty.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public abstract class NettyServiceTemplate {
	  /**
	   * NioEventLoop并不是一个纯粹的I/O线程，它除了负责I/O的读写之外
	     * 创建了两个NioEventLoopGroup，
	     * 它们实际是两个独立的Reactor线程池。
	     * 一个用于接收客户端的TCP连接，
	     * 另一个用于处理I/O相关的读写操作，或者执行系统Task、定时任务Task等。
     */
	static private EventLoopGroup bossGroup = new NioEventLoopGroup(100);
	static private EventLoopGroup workerGroup = new NioEventLoopGroup(100);

	abstract protected ChannelHandler[] createHandlers();

	abstract public int getPort();

	abstract public String getName();
	
	//@PostConstruct，在项目加载的时候启动，相当obj.start();
	@PostConstruct
	public void start() throws Exception {
		//ServerBootstrap负责初始化netty服务器，并且开始监听端口的socket请求
		ServerBootstrap b = new ServerBootstrap();
		b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
				.childHandler(new ChannelInitializer<SocketChannel>() {
					@Override
					public void initChannel(SocketChannel ch) throws Exception {
						ChannelHandler[] handlers = createHandlers();
						for (ChannelHandler handler : handlers) {
							ch.pipeline().addLast(handler);
						}
					}
				}).option(ChannelOption.SO_BACKLOG, 128) //当服务器请求处理线程全满时，用于临时存放已完成三次握手的请求的队列的最大长度。如果未设置或所设置的值小于1，Java将使用默认值50。
				.option(ChannelOption.SO_REUSEADDR, true)
				.childOption(ChannelOption.SO_KEEPALIVE, true)
				.childOption(ChannelOption.SO_REUSEADDR, true);

		ChannelFuture cf = b.bind(getPort()).await();
		// cf.channel().closeFuture().await();
		if (!cf.isSuccess()) {
			System.out.println("无法绑定端口：" + getPort());
			throw new Exception("无法绑定端口：" + getPort());
		}

		System.out.println("服务[{" + getName() + "}]启动完毕，监听端口[{" + getPort() + "}]");
	}

	@PreDestroy
	public void stop() {
		bossGroup.shutdownGracefully().syncUninterruptibly();
		workerGroup.shutdownGracefully().syncUninterruptibly();
		System.out.println("服务[{" + getName() + "}]关闭。");
	}
}
